/**
******************************************************************************
*
* @file       mapripper.cpp
* @author     The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010.
* @brief      A class that allows ripping of a selection of the map
* @see        The GNU Public License (GPL) Version 3
* @defgroup   OPMapWidget
* @{
*
*****************************************************************************/

/*
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
 * for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */

#include "mapripper.h"
#include <QRunnable>
#include <QEventLoop>

namespace mapcontrol {

MapRipper::MapRipper(internals::Core *core, const internals::RectLatLng &rect)
    : sleep(1), cancel(false), progressForm(0), core(core)
{
    if (!rect.IsEmpty()) {
        type = core->GetMapType();
        progressForm = new MapRipForm;
        area = rect;
        zoom = core->Zoom();
        maxzoom = core->MaxZoom();
        points = core->Projection()->GetAreaTileList(area, zoom, 0);
        this->start();
        progressForm->show();
        progressForm->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);

        connect(this, SIGNAL(percentageChanged(int)), progressForm,
                SLOT(SetPercentage(int)));
        connect(this, SIGNAL(numberOfTilesChanged(int, int)), progressForm,
                SLOT(SetNumberOfTiles(int, int)));
        connect(this, SIGNAL(providerChanged(QString, int)), progressForm,
                SLOT(SetProvider(QString, int)));
        connect(this, SIGNAL(finished()), this, SLOT(finish()));

        emit numberOfTilesChanged(0, 0);
    }
}

void MapRipper::finish()
{
    if (zoom < maxzoom) {
        ++zoom;

        /*
        QMessageBox msgBox;
        msgBox.setText(QString("Continue Ripping at zoom level %1?").arg(zoom));
        // msgBox.setInformativeText("Do you want to save your changes?");
        msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
        msgBox.setDefaultButton(QMessageBox::Yes);
        int ret = msgBox.exec();
        if(ret==QMessageBox::Yes)
        {
            points.clear();
            points=core->Projection()->GetAreaTileList(area, zoom, 0);
            this->start();
        }
        else
        {
            progressForm->close();
            delete progressForm;
            progressForm=NULL;
            this->deleteLater();
        }
        */

        points.clear();
        points = core->Projection()->GetAreaTileList(area, zoom, 0);
        this->start();
    } else {
        progressForm->close();
        delete progressForm;
        progressForm = NULL;

        this->deleteLater();
    }
}

class TileDownloadTask : public QRunnable
{
public:
    TileDownloadTask(MapRipper *ripper, int idx,
                     QVector<core::MapType::Types> types, int zoom, int all,
                     QVector<bool> *tileSuccess)
        : ripper(ripper), idx(idx), types(types), zoom(zoom), all(all),
          tileSuccess(tileSuccess)
    {
    }
    void run() override
    {
        if (ripper->cancel)
            return;
        core::Point p = ripper->points[idx];
        bool localGoodTile = false;
        foreach (core::MapType::Types t, types) {
            emit ripper->providerChanged(core::MapType::StrByType(t), zoom);
            QByteArray img = OPMaps::Instance()->GetImageFrom(t, p, zoom);
            if (img.length() != 0) {
                localGoodTile = true;
            }
        }
        {
            QMutexLocker locker(&ripper->progressMutex);
            (*tileSuccess)[idx] = localGoodTile;
            ripper->finishedCount++;
            emit ripper->numberOfTilesChanged(all, ripper->finishedCount);
            emit ripper->percentageChanged(
                (int)((ripper->finishedCount) * 100 / all));
        }
    }

private:
    MapRipper *ripper;
    int idx;
    QVector<core::MapType::Types> types;
    int zoom;
    int all;
    QVector<bool> *tileSuccess;
};

void MapRipper::run()
{
    // 每一级zoom都并发下载
    do {
        finishedCount = 0;
        QVector<core::MapType::Types> types =
            OPMaps::Instance()->GetAllLayersOfType(type);
        int all = points.count();
        QVector<bool> tileSuccess(all, false);
        threadPool.setMaxThreadCount(200); // 可调整并发数
        for (int i = 0; i < all; ++i) {
            TileDownloadTask *task =
                new TileDownloadTask(this, i, types, zoom, all, &tileSuccess);
            threadPool.start(task);
        }
        threadPool.waitForDone();
        // 检查失败的瓦片，重试一次（可选）
        QVector<int> failedIndices;
        for (int i = 0; i < all; ++i) {
            if (!tileSuccess[i])
                failedIndices.append(i);
        }
        if (!failedIndices.isEmpty()) {
            for (int idx : failedIndices) {
                TileDownloadTask *task = new TileDownloadTask(
                    this, idx, types, zoom, all, &tileSuccess);
                threadPool.start(task);
            }
            threadPool.waitForDone();
        }
        emit percentageChanged(100);
        // 进入下一级zoom
        if (zoom < maxzoom) {
            ++zoom;
            points.clear();
            points = core->Projection()->GetAreaTileList(area, zoom, 0);
        } else {
            break;
        }
    } while (true);
}

} // end of namespace mapcontrol
